#include "rvalue_ref.h"



int rvalue_ref() {
    //! 正常编译
    //! 编译器的优化不是C++语言的标准
    // 构造函数执行了:     1次   
    // 析构函数执行了:     1次    

    //! 禁用编译器优化 = > bazel 编译器设置: test --cxxopt='-fno-elide-constructors'  
    // 构造函数执行了:     1次    getA内A()构造了临时对象
    // 拷贝构造函数执行了:  1次    getA内A()构造的对象返回给A类型时产生的
    // 析构函数执行了:     1次    内部A()产生的临时对象析构了
    // 拷贝构造函数执行了:  2次    getA返回的值交给obj的时候发生的拷贝构造
    // 析构函数执行了:     2次    getA返回的临时对象析构了
    // 析构函数执行了:     3次    obj析构了
    A obj = getA();



    // 优化1: 将getA()返回的对象传给obj2时的拷贝构造优化掉
    /**
     * 构造函数执行了:     1次    getA内A()构造了临时对象
     * 拷贝构造函数执行了:  1次    getA内A()构造的对象返回给A类型时产生的
     * 析构函数执行了:     1次    内部A()产生的临时对象析构了
     * 析构函数执行了:     2次    obj析构了
     * */
    //! 通过右值引用将getA返回的临时变量绑定，延长了生命周期
    A&& obj2 = getA();



    // 优化2: 将getA()内部的临时对象也使用&&接收
    /**
     * 构造函数执行了:     1次    getA内A()构造了临时对象
     * 析构函数执行了:     1次    obj3析构了
     * */
    A&& obj3 = getAA();


    //! 常量左值引用(C++ 98/03实现右值引用的方式)
    /**
     * 构造函数执行了:     1次    getA内A()构造了临时对象
     * 拷贝构造函数执行了:  1次    getA内A()构造的对象返回给A类型时产生的
     * 析构函数执行了:     1次    内部A()产生的临时对象析构了
     * 析构函数执行了:     2次    ob4析构了
     * */
    // Notice: 我们发现其优化了外部obj4结构getA返回的临时对象，但getA内存的问题依然存在
    //! 常量左值应用时一个万能引用类型, 可以接受左值/右值/常量左值/常量右值
    const A& obj4 = getA();
    // A& obj4 = getA();                 // Error: 普通引用不接受右值值

    //! 2. 类型未定, 称为universal refenences, param有时是左值，有时是右值
    func(10);
    int x = 10;
    func(x);
    //       它只左值还是右值决定于它的初始化，如果T&&被左值初始化，其就是一个左值，如果被
    //       一个右值初始化，那它就是个右值.
    //! Notice: 只有发生自动类型推断时(模板参数推导，auto推导), &&才是universal ref.
    /**
     * @demo
     *    template<typename T>
     *    class Test {
     *       ...
     *       Test(Test&& rhs);                //! 类型确定，没有类型推断，为右值引用
     *    };
     * 
     *    template<typename T>
     *    void func(std::vector<T>&& param);  //! 右值引用, func调用时类型推断已经完成
     * 
     *    
     *    template<typename T>
     *    void func(const T&& param);         //! 右值引用, universal ref只在T&&发生，任何附加条件将使其失效
     * 
     */


    //! 3. 引用折叠
    /**
     * @brief T&&作为参数时，被左值或右值初始化，相比右值引用会发生类型变换，这种变化称为引用折叠.
     * 
     * ! C++11 中引用折叠规则如下：
     *    1).  所有的右值引用叠加到右值引用时还是右值引用;
     *    2).  所有的其它引用之间叠加将变成左值引用;
     *    
     */
    //! 已命名的右值变成左值
    int&&    var1 = 5;        //! 5是临时字面量，声明了一个右值引用去承接，承接后var1变更了左值

    // auto&& 是一个universal ref, 如果改成int&&则显式强调其为右值，但var1为左值
    auto&&   var2 = var1;     //! 因为var1是左值， var2会被推导为左值，如果将auto改为int,则会编译报错


    //! 4. 转发
    int i = 0;
    printValue(i);
    printValue(0);
    forward(3); 
    perfectForward(3);

    //! 5. 右值引用优化性能，避免深拷贝
    /**
     * @brief 
     *  construct    // 构造b
     *  destruct     // 内存返回b给形参时, 析构一次
     *  destruct     // getB给ovj5之后,  析构一次
     * free(): double free detected in tcache 2    //! 浅拷贝, 都指向统一指针
     *! TODO: 增加深拷贝的构造函数 
     */

    //! 增加深拷贝构造函数之后
    /**
     * @brief 
     *   construct
     *   copy construct
     *   destruct
     *   copy construct
     *   destruct
     *   destruct
     */
    //! 痛点: getB返回的临时对象在拷贝构造给obj5后就回收了，如果深拷贝代价很高，这种就会带来较大的性能损耗
    //! 拷贝构造函数的参数有const，即常量左值引用，其是可以接收右值的，我们可以考虑将右值单独拎出来实现右值拷贝构造函数
    B  obj5 = getB();

    //! 改进getB的调用
    /**
     *  1). 减少getB拷贝给obj5后的消亡: B&& obj5 = getB();
     *  2). 减少内部b传递给返回参数后的消亡,：
     *  
     *    B&& getBB(bool flag = false) {
     *        B b;
     *        return std::move(b);
     *    }
     *  
     *  construct
     *  destruct               //! move 构造直接被消掉了
     */
    B&& obj6 = getBB();
    return 0;
}